﻿using System;
using System.Globalization;
using System.Runtime.InteropServices;

namespace Microsoft.Xna.Framework.Input
{
    /// <summary>
    /// Represents specific information about the state of an Xbox 360 Controller, including the current state of buttons and sticks.
    /// </summary>
	[StructLayout(LayoutKind.Sequential)]
    public struct GamePadState
	{
		#region Fields
		GamePadThumbSticks _thumbSticks;
        GamePadTriggers _triggers;
        GamePadDPad _dpad;
        GamePadButtons _buttons;
		bool isConnected;
		#endregion

		#region Properties
		/// <summary>
		/// Returns a structure that identifies what buttons on the Xbox 360 controller are pressed.
		/// </summary>
		public GamePadButtons Buttons
		{
			get { return _buttons; }
		}

		/// <summary>
		/// Returns a structure that identifies what directions of the directional pad on the Xbox 360 Controller are pressed.
		/// </summary>
		public GamePadDPad DPad
		{
			get { return _dpad; }
		}

		/// <summary>
		/// Indicates whether the Xbox 360 Controller is connected.
		/// </summary>
		public bool IsConnected
		{
			get { return isConnected; }
		}

		/// <summary>
		/// Returns a structure that indicates the position of the Xbox 360 Controller sticks (thumbsticks).
		/// </summary>
		public GamePadThumbSticks ThumbSticks
		{
			get { return _thumbSticks; }
		}

		/// <summary>
		/// Returns a structure that identifies the position of triggers on the Xbox 360 controller.
		/// </summary>
		public GamePadTriggers Triggers
		{
			get { return _triggers; }
		}
		#endregion

		#region Constructors
		/// <summary>
		/// Initializes a new instance of the GamePadState class using the specified GamePadThumbSticks, GamePadTriggers, GamePadButtons, and GamePadDPad.
		/// </summary>
		/// <param name="thumbSticks">Initial thumbstick state.</param>
		/// <param name="triggers">Initial trigger state.</param>
		/// <param name="buttons">Initial button state.</param>
		/// <param name="dPad">Initial directional pad state.</param>
		public GamePadState(GamePadThumbSticks thumbSticks, GamePadTriggers triggers, GamePadButtons buttons, GamePadDPad dPad)
		{
			isConnected = true;
			_thumbSticks = thumbSticks;
			_triggers = triggers;
			_dpad = dPad;
			_buttons = buttons;
		}

		/// <summary>
		/// Initializes a new instance of the GamePadState class with the specified stick, trigger, and button values.
		/// </summary>
		/// <param name="leftThumbStick">Left stick value. Each axis is clamped between −1.0 and 1.0.</param>
		/// <param name="rightThumbStick">Right stick value. Each axis is clamped between −1.0 and 1.0.</param>
		/// <param name="leftTrigger">Left trigger value. This value is clamped between 0.0 and 1.0.</param>
		/// <param name="rightTrigger">Right trigger value. This value is clamped between 0.0 and 1.0.</param>
		/// <param name="buttons">Array or parameter list of Buttons to initialize as pressed.</param>
		public GamePadState(Vector2 leftThumbStick, Vector2 rightThumbStick, float leftTrigger, float rightTrigger, params Buttons[] buttons)
		{
			isConnected = true;
			_thumbSticks = new GamePadThumbSticks(leftThumbStick, rightThumbStick);
			_triggers = new GamePadTriggers(leftTrigger, rightTrigger);
			_dpad = new GamePadDPad();

			Buttons buttons2 = 0;

			foreach (Buttons button in buttons)
			{
				buttons2 |= button;
			}

			_buttons = new GamePadButtons(buttons2);
			_dpad._down = ((buttons2 & Input.Buttons.DPadDown) == Input.Buttons.DPadDown) ? ButtonState.Pressed : ButtonState.Released;
			_dpad._left = ((buttons2 & Input.Buttons.DPadLeft) == Input.Buttons.DPadLeft) ? ButtonState.Pressed : ButtonState.Released;
			_dpad._right = ((buttons2 & Input.Buttons.DPadRight) == Input.Buttons.DPadRight) ? ButtonState.Pressed : ButtonState.Released;
			_dpad._up = ((buttons2 & Input.Buttons.DPadUp) == Input.Buttons.DPadUp) ? ButtonState.Pressed : ButtonState.Released;
		}
		#endregion

        #region Public Methods
		/// <summary>
		/// Returns a value that indicates whether the current instance is equal to a specified object.
		/// </summary>
		/// <param name="obj">Object with which to make the comparison.</param>
		/// <returns>true if this instance is equal to the specified object; otherwise, false.</returns>
		public override bool Equals(object obj)
		{
			if (obj is GamePadState)
				return this == (GamePadState)obj;
			return false;
		}

		/// <summary>
		/// Gets the hash code for this instance.
		/// </summary>
		public override int GetHashCode()
		{
			return (((this._thumbSticks.GetHashCode() ^ this._triggers.GetHashCode()) ^ (this._buttons.GetHashCode() ^ this.isConnected.GetHashCode())) ^ this._dpad.GetHashCode());
		}

        /// <summary>
        /// Determines whether specified input device buttons are pressed in this GamePadState.
        /// </summary>
        /// <param name="b">Buttons to query. Specify a single button, or combine multiple buttons using a bitwise OR operation.</param>
        public bool IsButtonDown(Buttons b)
        {
			return !IsButtonUp(b);
        }
        /// <summary>
        /// Determines whether specified input device buttons are up (not pressed) in this GamePadState.
        /// </summary>
        /// <param name="b">Buttons to query. Specify a single button, or combine multiple buttons using a bitwise OR operation.</param>
        public bool IsButtonUp(Buttons b)
        {
			switch (b)
			{
				case Input.Buttons.A:
					return (Buttons.A == ButtonState.Released);
				case Input.Buttons.B:
					return (Buttons.B == ButtonState.Released);
				case Input.Buttons.Back:
					return (Buttons.Back == ButtonState.Released);
				case Input.Buttons.BigButton:
					return (Buttons.BigButton == ButtonState.Released);
				case Input.Buttons.DPadDown:
					return (DPad.Down == ButtonState.Released);
				case Input.Buttons.DPadLeft:
					return (DPad.Left == ButtonState.Released);
				case Input.Buttons.DPadRight:
					return (DPad.Right == ButtonState.Released);
				case Input.Buttons.DPadUp:
					return (DPad.Up == ButtonState.Released);
				case Input.Buttons.LeftShoulder:
					return (Buttons.LeftShoulder == ButtonState.Released);
				case Input.Buttons.LeftStick:
					return (Buttons.LeftStick == ButtonState.Released);
				case Input.Buttons.LeftThumbstickDown:
					return (ThumbSticks.Left.Y <= 0.16f);
				case Input.Buttons.LeftThumbstickLeft:
					return (ThumbSticks.Left.X >= -0.16f);
				case Input.Buttons.LeftThumbstickRight:
					return (ThumbSticks.Left.X <= 0.16f);
				case Input.Buttons.LeftThumbstickUp:
					return (ThumbSticks.Left.Y >= -0.16f);
				case Input.Buttons.LeftTrigger:
					return (Triggers.Left <= 0.16f);
				case Input.Buttons.RightShoulder:
					return (Buttons.RightShoulder == ButtonState.Released);
				case Input.Buttons.RightStick:
					return (Buttons.RightStick == ButtonState.Released);
				case Input.Buttons.RightThumbstickDown:
					return (ThumbSticks.Right.Y <= 0.16f);
				case Input.Buttons.RightThumbstickLeft:
					return (ThumbSticks.Right.X >= -0.16f);
				case Input.Buttons.RightThumbstickRight:
					return (ThumbSticks.Right.X <= 0.16f);
				case Input.Buttons.RightThumbstickUp:
					return (ThumbSticks.Right.Y >= -0.16f);
				case Input.Buttons.RightTrigger:
					return (Triggers.Right <= 0.16f);
				case Input.Buttons.Start:
					return (Buttons.Start == ButtonState.Released);
				case Input.Buttons.X:
					return (Buttons.X == ButtonState.Released);
				case Input.Buttons.Y:
					return (Buttons.Y == ButtonState.Released);
				default:
					throw new ArgumentOutOfRangeException("Invalid enumeration value.");
			}
        }

		/// <summary>
		/// Retrieves a string representation of this object.
		/// </summary>
		public override string ToString()
		{
			return string.Format(CultureInfo.CurrentCulture, "{{IsConnected:{0}}}", isConnected);
		}
        #endregion

		#region Operators
		/// <summary>
		/// Determines whether two GamePadState instances are equal.
		/// </summary>
		/// <param name="left">Object on the left of the equal sign.</param>
		/// <param name="right">Object on the right of the equal sign.</param>
		/// <returns>true if both instances are equal; otherwise, false.</returns>
		public static bool operator ==(GamePadState left, GamePadState right)
		{
			return ((left._buttons == right._buttons) && (left._dpad == right._dpad) &&
					(left._thumbSticks == right._thumbSticks) && (left._triggers == right._triggers) &&
					(left.isConnected == right.isConnected));
		}

		/// <summary>
		/// Determines whether two GamePadState instances are not equal.
		/// </summary>
		/// <param name="left">Object on the left of the inequal sign.</param>
		/// <param name="right">Object on the right of the inequal sign.</param>
		/// <returns>true if the instances are not equal; otherwise, false.</returns>
		public static bool operator !=(GamePadState left, GamePadState right)
		{
			return ((left._buttons != right._buttons) || (left._dpad != right._dpad) ||
					(left._thumbSticks != right._thumbSticks) || (left._triggers != right._triggers) ||
					(left.isConnected != right.isConnected));
		}
		#endregion
	}
}
